# -*- coding: utf-8 -*-

#Node
#  CompositeNode
#    Root
#    Repository
#    Project
#    Dir
#  File


import unittest
import os
import sys
import shutil 
import cPickle
import stat


import pystore.core._remote_service as _remote_service
import pystore.core.config as config
import pystore.core.resource_type as resource_type
import pystore.core.i18n as i18n

import pystore.core.framework.os2 as os2
import pystore.core.framework.util as util
import pystore.core.framework.job as job
import pystore.core.framework.logger as logger

class _LocalStore(object):
    '''
     node 下部 file system store
    '''
    def __init__(self,node):
        self.__node=node
    
    def checkAvailability(self):
        if self.__node.type() is resource_type.ROOT or \
           self.__node.type() is resource_type.REPOSITORY:
            raise Exception('ROOT and REPOSITORY have not _LocalStore')
    
    def exists(self):
        self.checkAvailability()
        if not self.__node.project()._isBinding() :
            return False
        return os.path.exists( self.path() )

    def parent(self):
        self.checkAvailability()
        return self.__node.parent().store()

    def path(self):
        """return FileSystem path of this node """
        self.checkAvailability()
        if not self.__node.project()._isBinding() :
            raise Exception('not binding project')
        
        if self.__node.type() is resource_type.PROJECT:
            return self.__node.project()._bindingPath
        return os.path.join(self.parent().path(),self.__node.name())

class NodeManager():
    def __init__(self):
       self.root = Root(self)
       self.__addedListeners = []
       
    def addDiskRepository(self,storeLocation):
        '''
        addDiskRepository(basestring storeLocation)
    
         在指定位置添加一个磁盘资源库
        '''
        return self.root.addRepository(Repository(self.root,storeLocation,_remote_service.DiskRemoteService(storeLocation)))
       
    def addNodeAddedListener(self,onAddedCallback):
        """
        addNodeAddedListener(self,func onAddedCallback)
        
        args:
          onAddedCallback : onAddedCallback(Node addedNode)
        """
        self.__addedListeners.append(onAddedCallback)

    def addProject(self,repository,name,path,monitor = job.DefaultMonitor()):
        """
        addProject(Repository repository,basestring name,basestring path,IMonitor monitor = job.DefaultMonitor()):
        """
        return repository._addProject(name,path,monitor)

    def _fireAddNode(self,addedNode):
        map(lambda callback:callback(addedNode),self.__addedListeners)
    
class Node(object):
    def __init__(self,parent,name):
        self.__parent=parent
        self.__name=name
        self._store = _LocalStore(self)
        
    def __str__(self):
        return '%s(%s)' % (self.__name,self.type())
    
    def store(self):
        return self._store
    
    def manager(self):
        return self.__parent.manager()
    
    def type(self):
        raise NotImplementedError           

    def parent(self):
        return self.__parent
    
    def project(self):
        current = self
        while True:
            if isinstance(current,Project) or current is None: return current
            current = current.parent()
        #不应该在Root或Repository层级调用project()
        raise Excpetion("not have project parent")
    
    def name(self):
        return self.__name

    def walk(self,walkFunc,fromTopToBottom=True):
        """
         参考IWalkNodeAble.walk
        """

        getChildrenFunc=lambda n : [] if isinstance(n,File) else n._children.values()
        util.walkFunc(getChildrenFunc)(self,walkFunc,fromTopToBottom)
    
    def localResource(self):
        return None
    
    def isRoot(self):
        return self.parent() is None
    
    def path(self):
        if self.isRoot() :
            return u'\\'
        if self.type() is resource_type.REPOSITORY:
            return u"<%s>" % self.name()
        return os.path.join(self.parent().path(),self.name())
    

class CompositeNode(Node):
    def __init__(self,parent,name):
        Node.__init__(self,parent,name)
        self._children={}

    def has_key(self,name):
        return self._children.has_key(name)
    
    def _add(self,child):
        self._children[child.name()] = child
        self.manager()._fireAddNode(child)
        return child
    

class Root(CompositeNode):
    def __init__(self,manager):
        CompositeNode.__init__(self,None,'root')
        self.__manager = manager
        
    def manager(self):
        return self.__manager

    def addRepository(self,repository):
        return self._add(repository)
    
    def type(self):
        return resource_type.ROOT 
    
class Repository(CompositeNode):
    def __init__(self,parent,name,remoteService):
        CompositeNode.__init__(self, parent, name)
        
    def _addProject(self,name,path,monitor):
        project = self._add( Project(self,name,path))
        project.localLoad(monitor)
        return project

    def type(self):
        return resource_type.REPOSITORY 
 
class Dir(CompositeNode):
    def __init__(self,parent,name):
        CompositeNode.__init__(self,parent,name)

    def localLoad(self,monitor=job.DefaultMonitor()):
        #todo assert self.status() != node_status.NOT_OPEN
        assert self.project()._isBinding()
        
        if not self.store().exists():
            return 
        
        children = os.listdir( self.store().path() )
        
        if len(children)==0 :
            return 
        
        monitor.beginTask(_t('load files'),len(children))
        try:
            for childName in children:
                if monitor.isCanceled():
                    break 
                
                fullpath=os.path.join( self.store().path(), childName)
                
                monitor.setTaskName(fullpath)
                monitor.worked(1)
                #_log.info(fullpath)
                if os.path.isfile(fullpath) and not self.has_key(childName):
                    self._add(File(self,childName))
                    continue
                if os.path.isdir( fullpath ) and not self.has_key(childName):
                    self._add(Dir(self,childName) ).localLoad(job.SubMonitor(monitor,1))#job.SubMonitor(monitor)
        finally:
            monitor.done()
    
    
    
    def type(self):
        return resource_type.DIR 
    
class Project(Dir):
    
    def __init__(self,parent,name,bindingPath):
        CompositeNode.__init__(self, parent, name)
        self._bindingPath = bindingPath
        
    def _isBinding(self):
        return self._bindingPath is not None
    
    def type(self):
        return resource_type.PROJECT 
    
class File(Node):
    def __init__(self,parent,name):
        Node.__init__(self,parent,name)
        
    def type(self):
        return resource_type.FILE
    
_t=i18n.t_
_log = logger.log